#include <stdx/all.h>

static const char* GetTemplateConfigString()
{
	static u_char data[] = {
		0x2D,0x2D,0x20,0x63,0x6F,0x6D,0x70,0x69,0x6C,0x65,0x72,0x0A,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0A,0x43,
		0x43,0x20,0x3D,0x20,0x22,0x67,0x2B,0x2B,0x22,0x0A,0x0A,0x2D,0x2D,0x20,0x63,0x6F,
		0x6D,0x70,0x69,0x6C,0x65,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x0A,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x0A,0x46,0x4C,0x41,0x47,0x20,0x3D,0x20,0x22,0x2D,0x67,0x22,0x0A,0x0A,0x2D,0x2D,
		0x20,0x6C,0x69,0x62,0x72,0x61,0x72,0x79,0x20,0x6C,0x69,0x6E,0x6B,0x20,0x73,0x65,
		0x74,0x74,0x69,0x6E,0x67,0x0A,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
		0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0A,0x4C,0x49,0x42,0x5F,0x4C,0x49,0x4E,
		0x4B,0x20,0x3D,0x20,0x22,0x22,0x00
	};

	return (const char*)(data);
}


class CppShellApplication : public Application
{
protected:
	time_t ts;
	string cmd;
	string appname;
	string exepath;
	ConfigFile cfg;

	bool loadConifg()
	{
		time_t tm;
		size_t pos;
		string binpath;
		string cfgpath;
		const char* param = GetCmdParam(1);
		string path = stdx::GetParentPath(param);

		if (path.empty())
		{
			path = Process::GetCurrentDirectory();
			if (path.empty()) path = ".";
			appname = param;
		}
		else
		{
			appname = stdx::GetFileNameFromPath(param);
		}

		pos = appname.rfind('.');

		if (string::npos != pos)
		{
			appname = appname.substr(0, pos);
		}

		binpath = path + "/.bin";

		exepath += binpath + "/" + appname;

#ifndef XG_LINUX
		exepath += ".exe";
#endif
		cfgpath = path + "/config.lua";
		
		tm = GetFileUpdateTime(exepath.c_str());
		ts = GetFileUpdateTime(param);
		
		if (XFile::GetFileSize(cfgpath) > 0)
		{
			if (tm > ts && tm > GetFileUpdateTime(cfgpath.c_str())) return true;

			CHECK_FALSE_RETURN(cfg.open(cfgpath));
		}
		else
		{
			if (tm > ts) return true;

			char buffer[MAX_PATH];

			CHECK_FALSE_RETURN(cfg.init(NULL));
#ifdef XG_AIX
			cfg.setVariable("CC", "xlC_r");
#else	
			if (RunCommand("which g++", buffer, sizeof(buffer), TRUE) && *buffer)
			{
				cfg.setVariable("CC", "g++");
			}
			else
			{
				cfg.setVariable("CC", "gcc");
			}
#endif
		}

		string cc = cfg.getVariable("CC");
		string flag = cfg.getVariable("FLAG");
		string link = cfg.getVariable("LIB_LINK");

		link = stdx::replace(link, "\t", " ");

		auto check = [&](const string& lib){
			string tag = " " + lib + " ";
			string tmp = " " + link + " ";
			if (tmp.find(tag) == string::npos) link += " " + lib;
		};

		check("-pthread");

#ifdef XG_LINUX
		if (flag.find("-DXG_LINUX") == string::npos)
		{
			flag = "-DXG_LINUX " + flag;
		}

		check("-lutil");
		check("-ldl");
#else
		if (flag.find("-DXG_LINUX") != string::npos)
		{
			flag = stdx::replace(flag, "-DXG_LINUX", "");
			flag = stdx::trim(flag);
		}

		check("-lws2_32");
		check("-lpsapi");
#endif
		check("-lm");

		stdx::format(cmd, "%s %s -o %s %s %s", cc.c_str(), flag.c_str(), exepath.c_str(), param, link.c_str());

		if (cmd[0] == ' ') return false;

		cmd = stdx::trim(cmd);
		cmd = stdx::replace(cmd, "  ", " ");
		
		CreateFolder(binpath.c_str());
				
		return true;
	}

public:
	bool main()
	{
		string msg;
		const char* param = NULL;
		int cnt = GetCmdParamCount();
		static char buffer[40960] = { 0 };

		if ((param = GetCmdParam("-etc")))
		{
			if (*param)
			{
				TextFile out;
				
				if (GetCmdParam("-i") && XFile::GetFileSize(param) > 0)
				{
					CHECK_FALSE_RETURN(cmdx::CheckCommand("file[%s] exists, overwrite or not ? (y/n)", stdx::GetFileNameFromPath(param).c_str()));
				}

				if (out.open(param, true))
				{
					out.puts(GetTemplateConfigString());

					puts("export template config success");
				}
				else
				{
					puts("export template config failed");

					return false;
				}
			}
			else
			{
				puts(GetTemplateConfigString());
			}

			return true;
		}
		
		if (GetCmdParam(1) == NULL)
		{
			msg = "please input source file";
			
			goto __ERROR__;
		}

		if (XFile::GetFileSize(GetCmdParam(1)) < 0)
		{
			msg = "load source file failed";
			
			goto __ERROR__;
		}
		
		loadConifg();

		if (exepath.empty())
		{
			msg = "load build-essential failed";

			goto __ERROR__;
		}

		if (cmd.length() > 0)
		{
			if (RunCommand(cmd.c_str(), buffer, sizeof(buffer), TRUE) > 0)
			{
				msg = buffer;

				goto __ERROR__;
			}
			
			if (GetFileUpdateTime(exepath.c_str()) < ts)
			{
				msg = "compile failed";

				goto __ERROR__;
			}
		}

		cmd = exepath;

		for (int i = 2; i < cnt; i++)
		{
			cmd += " \"";
			cmd += GetCmdParam(i);
			cmd += "\"";
		}

		return system(cmd.c_str()) >= 0;

	__ERROR__:

		if (msg.length() > 0) ColorPrint(eRED, "%s\n", msg.c_str());

#ifndef XG_LINUX
		if (Process::IsStartedByExplorer()) SystemPause(NULL);
#endif
		return false;
	}
};

START_APP(CppShellApplication)